package GraphCut;

import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import java.awt.event.*;
import java.util.LinkedList;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import ij.plugin.frame.*;

@SuppressWarnings("serial")
/*w tym oknie wyswietlany jest obrazek, oprocz tego odpalenie tego okna powoduje wrzucenie okienka z operacjami*/
public class GraphCutWindow extends JFrame implements ActionListener, ChangeListener {

	/*okienko operacyjne*/
	JButton start_button;
	JButton close_button;
	JPanel controlPanel;
	JButton addObjectRoiButton;
	JButton remObjectRoiButton;
	JButton addBackRoiButton;
	JButton remBackRoiButton;
	JSlider roSlider;
	JSlider lambdaSlider;
	JRadioButton simple;
	JRadioButton potts;
	JButton resetButton;
	/*miejsca na histogram tla i obiektu*/
	int [] histoObj;
	int [] histoBack;
	/*tutaj przechowujemy graf*/
	Graph graph;
	ImagePlus processedImage;
	public GraphCutWindow(ImagePlus imp){
		super();
		this.setTitle("Graph Cut Plugin");
		this.setPreferredSize(new Dimension(300,400));
		/*alokujemy se pamiec na wszystko*/
		processedImage = imp;
		graph = new Graph(imp.getWidth(), imp.getHeight(), imp.getProcessor());
		histoObj = new int[256];
		histoBack = new int[256];
		setMinMaxHisto();
		/////////////////////////////////////////////////////////////////////
		/*tworzenie wygladu okienka operacyjnego, wypadaloby zmienic layout, ale nie powinno byc zle teraz*/
		
		controlPanel = new JPanel();
		controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.PAGE_AXIS));	
		start_button = new JButton("Graph Cut");
		start_button.setAlignmentX(Component.CENTER_ALIGNMENT);
		start_button.addActionListener(this);
		start_button.setEnabled(false);
		close_button = new JButton("Exit");
		close_button.setAlignmentX(Component.CENTER_ALIGNMENT);
		close_button.addActionListener(this);
		controlPanel.add(start_button);
		controlPanel.add(close_button);
		JPanel roiObjPanel = new JPanel();
		JPanel roiBackPanel = new JPanel();
		roiObjPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
		roiBackPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
		addObjectRoiButton = new JButton("+");
		addObjectRoiButton.addActionListener(this);
		addBackRoiButton = new JButton("+");
		addBackRoiButton.addActionListener(this);
		remObjectRoiButton = new JButton("-");
		remObjectRoiButton.addActionListener(this);
		remObjectRoiButton.setEnabled(false);
		remBackRoiButton = new JButton("-");
		remBackRoiButton.addActionListener(this);
		remBackRoiButton.setEnabled(false);
		roiObjPanel.add(addObjectRoiButton);
		roiObjPanel.add(remObjectRoiButton);
		roiObjPanel.add(new JLabel("Obiekt"));
		
	    roiBackPanel.add(addBackRoiButton);
	    roiBackPanel.add(remBackRoiButton);
	    roiBackPanel.add(new JLabel("Tlo"));
	   
	    roSlider = new JSlider(JSlider.HORIZONTAL,10,1000, 100);
	    roSlider.setMajorTickSpacing(90);
	    roSlider.setMinorTickSpacing(10);
	    roSlider.setPaintTicks(true);
	    roSlider.setPaintLabels(true);
	    roSlider.addChangeListener(this);
	    roSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
	    roSlider.setEnabled(false);
	    lambdaSlider = new JSlider(JSlider.HORIZONTAL,10,1000, 100);
	    lambdaSlider.setMajorTickSpacing(90);
	    lambdaSlider.setMinorTickSpacing(10);
	    lambdaSlider.setPaintTicks(true);
	    lambdaSlider.setPaintLabels(true);
	    lambdaSlider.addChangeListener(this);
	    lambdaSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
	    lambdaSlider.setEnabled(false);
	    
	    simple = new JRadioButton("Proste wagi");
	    simple.setSelected(true);
	    simple.setAlignmentX(Component.CENTER_ALIGNMENT);
	    potts = new JRadioButton("Wagi model Pottsa");
	    potts.setSelected(false);
	    potts.setAlignmentX(Component.CENTER_ALIGNMENT);
	    
	    simple.addActionListener(this);
	    potts.addActionListener(this);
	    ButtonGroup weights = new ButtonGroup();
	    weights.add(simple);
	    weights.add(potts);
	    
	    resetButton = new JButton("Wyczysc");
	    resetButton.addActionListener(this);
	    resetButton.setAlignmentX(Component.CENTER_ALIGNMENT);
	    
	    controlPanel.add(roiObjPanel);
	   
	    controlPanel.add(roiBackPanel);
	    
	    controlPanel.add(simple);
	    controlPanel.add(potts);
	    JLabel tmp = new JLabel("Ro");
	    tmp.setAlignmentX(Component.CENTER_ALIGNMENT);
	    controlPanel.add(tmp);
	    controlPanel.add(roSlider);
	    tmp = new JLabel("Lambda");
	    tmp.setAlignmentX(Component.CENTER_ALIGNMENT);
	    controlPanel.add(tmp);
	    controlPanel.add(lambdaSlider);
	    controlPanel.add(resetButton);
		add(controlPanel);
		
	
		pack();
		setVisible(true);
		IJ.log(processedImage.getTitle() + " zaladowany do Graph Cut");
		IJ.log("Po dodaniu obszarow kliknac przycisk Graph Cut");
		IJ.log("Ponowna segmentacja (i zaznaczanie obszarow) mozliwa po uzyciu przycisku Wyczysc");
		/////////////////////////////////////////////////////////////////
	}
	
	@Override
	public void actionPerformed(ActionEvent arg0) {
		if(arg0.getSource() == close_button)
		{
			this.dispose();
		}
		else if(arg0.getSource() == addObjectRoiButton)
		{
			/*
			 * pobieramy histogram zaznaczonego obszaru(zaznaczamy freehand roi wylacznie)
			 * ustawiamy ziarna dla zrodla(ziarno == krawedz od zrodla do piksela ma okolo nieskonczona pojemnosc)
			 */
			 histoObj =processedImage.getProcessor().getHistogram();
			 graph.setHistogram(histoObj, true);
			 Rectangle roi = processedImage.getProcessor().getRoi();
			 setSourceSeeds(roi.x,roi.y,processedImage.getMask().duplicate());
			 processedImage.killRoi();
			 addObjectRoiButton.setEnabled(false);
			 if(!addBackRoiButton.isEnabled())
				 start_button.setEnabled(true);
			 remObjectRoiButton.setEnabled(true);
		}
		else if(arg0.getSource() == addBackRoiButton)
		{
			/*
			 * j.w. tylko dla tla
			 */
			 histoBack = processedImage.getProcessor().getHistogram();
			 graph.setHistogram(histoBack, false);
			 Rectangle roi = processedImage.getProcessor().getRoi();
			 setSinkSeeds(roi.x,roi.y,processedImage.getMask().duplicate());
			 processedImage.killRoi();
			 addBackRoiButton.setEnabled(false);
			 if(!addObjectRoiButton.isEnabled())
				 start_button.setEnabled(true);
			 remBackRoiButton.setEnabled(true);
		}
		else if(arg0.getSource() == start_button)
		{
			/*
			 * ustawiamy krawedzie miedzy pikselami i krawedzie miedzy zrodlem a pikselem i pikselem a zlewem(sink ;) )
			 * odpalamy algorytm fordafulkersona, poki co na razie algorytm powinien liczyc maksymalny przeplyw, jesli maksymalny przeplyw bedzie wygladal ok
			 * to wtedy sie zmodyfikuje zeby zaznaczal odpowiednie piksele do odpowiednio zrodla i zlewu
			 */
			remObjectRoiButton.setEnabled(false);
			remBackRoiButton.setEnabled(false);
			if(addObjectRoiButton.isEnabled()||addBackRoiButton.isEnabled())
				return;
			Integer time =(int)System.currentTimeMillis();
					graph.setEdges();
			time = (int)System.currentTimeMillis()-time;
			IJ.log("Zakonczono ustawianie krawedzi w czasie: "+time.toString()+" ms");
			
			time =(int) System.currentTimeMillis();
			Integer flow = new Integer((int)graph.GraphCutDinic());
			time = (int)System.currentTimeMillis()-time;
					IJ.log("Zakonczono segmentacje w czasie: "+
							time.toString()+ " ms"+
							"\nMaksymalny przeplyw: " +flow.toString());
					byte [] mask = graph.getSegmentation();
					ImagePlus maskImage = new ImagePlus("GraphCut mask", new ByteProcessor(processedImage.getWidth(), processedImage.getHeight(), mask));
					new ImageWindow(maskImage).setVisible(true);
			
			
			start_button.setEnabled(false);
		}
		else if(arg0.getSource()==simple)
		{
			graph.setSimpleWeights(simple.isSelected());
			roSlider.setEnabled(!simple.isSelected());
			lambdaSlider.setEnabled(!simple.isSelected());
		}
		else if(arg0.getSource()==potts)
		{
			graph.setSimpleWeights(!potts.isSelected());
			roSlider.setEnabled(potts.isSelected());
			lambdaSlider.setEnabled(potts.isSelected());
		}
		else if(arg0.getSource()==resetButton)
		{
			graph.clear();
			addObjectRoiButton.setEnabled(true);
			addBackRoiButton.setEnabled(true);
			remObjectRoiButton.setEnabled(false);
			remBackRoiButton.setEnabled(false);
		}
		else if(arg0.getSource()==remObjectRoiButton)
		{
			graph.clearHistogram(true);
			addObjectRoiButton.setEnabled(true);
			remObjectRoiButton.setEnabled(false);
		}
		else if(arg0.getSource()==remBackRoiButton)
		{
			graph.clearHistogram(false);
			addObjectRoiButton.setEnabled(true);
			remBackRoiButton.setEnabled(false);
		}
	}
	private void setSourceSeeds(int x, int y, ImageProcessor mask) {
		for(int i = 0; i<mask.getWidth();i++)
		{
			for(int j = 0; j<mask.getHeight();j++)
			{
				if(mask.get(i, j)>0)
				{
					graph.setNodeTerminal((i+x)+(j+y)*(processedImage.getWidth()), Graph.SOURCE);
				}
			}
		}
		
	}
	private void setSinkSeeds(int x, int y, ImageProcessor mask) {
		for(int i = 0; i<mask.getWidth();i++)
		{
			for(int j = 0; j<mask.getHeight();j++)
			{
				if(mask.get(i, j)>0)
				{
					graph.setNodeTerminal((i+x)+(j+y)*(processedImage.getWidth()), Graph.SINK);
				}
			}
		}
	}
	
	
	private void setMinMaxHisto()
	{
		int [] histogram = processedImage.getProcessor().getHistogram();
		for(int i = 0; i<histogram.length;++i)
		{
			if(histogram[i]>0)
			{
				graph.setMinI(i);
				break;
			}
		}
		for(int i = histogram.length-1; i>=0;--i)
		{
			if(histogram[i]>0)
			{
				graph.setMaxI(i);
				break;
			}
		}
	}

	@Override
	public void stateChanged(ChangeEvent e) {
		JSlider sl =(JSlider) e.getSource();
		if(!sl.getValueIsAdjusting())
		{
			if(sl==roSlider)
			{
				graph.setWeights(true,sl.getValue());
			}
			else
			{
				graph.setWeights(false,sl.getValue());
			}
		}
	}
	
}
